/*
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "config.h"
#include "CookieJar.h"

#if !USE(NTF)

#include "Cookie.h"
#include "Document.h"
#include "KURL.h"
#include "NotImplemented.h"

#if PLATFORM(MANX)
#include "ResourceHandleManager.h"
#endif

#include <wtf/HashMap.h>
#include <wtf/text/StringHash.h>

namespace WebCore {

#if !PLATFORM(MANX)
static HashMap<String, String> cookieJar;
#endif

String cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const KURL& /*firstParty*/, const KURL& url)
{
#if PLATFORM(MANX)
    return ResourceHandleManager::sharedInstance()->cookies(url);
#else
    // FIXME: include HttpOnly cookie.
    return cookieJar.get(url.string());
#endif
}

String cookiesForDOM(const NetworkStorageSession& session, const KURL&, const KURL& url)
{
    return ResourceHandleManager::sharedInstance()->cookies(url);
}

void setCookiesFromDOM(const NetworkStorageSession&, const KURL& firstParty, const KURL& url, const String& value)
{
    ResourceHandleManager::sharedInstance()->setCookies(url, value);
}

bool cookiesEnabled(const NetworkStorageSession& session, const KURL& /*firstParty*/, const KURL& /*url*/)
{
    return true;
}

static bool isMatchDomain(const KURL& url, String& domain)
{
    if (domain[0] == '.')
        return url.host().endsWith(domain, false);
    return (url.host().lower() == domain.lower());
}

static bool getCookiesFromCurlCookieList(const KURL& url, Vector<Cookie>& rawCookies)
{
    String cookie;
    static const String emptyString;
    struct curl_slist *list;

    CURL* handle = curl_easy_init();
    curl_easy_setopt(handle, CURLOPT_SHARE, ResourceHandleManager::curlShareHandle());
    curl_easy_setopt(handle, CURLOPT_URL, url.string().latin1().data());
    
    ResourceHandleManager::lockCurlSharedResource(CURL_LOCK_DATA_COOKIE);
    curl_easy_getinfo(handle, CURLINFO_COOKIELIST, &list);
    ResourceHandleManager::unlockCurlSharedResource(CURL_LOCK_DATA_COOKIE);
    
    curl_easy_cleanup(handle);

    if (!list)
        return false;

    for (struct curl_slist *cur = list; cur; cur = cur->next) {
        String data(cur->data);

        if (!cur->data)
            continue;

        Vector<String> bits;
        data.split("\t", bits);

        double expires = bits[4].toDouble() * 1000;
        // FIXME: a marker for deleted cookie is that "expires" value is 1.
        //        But the value can be easily set on JavaScript.
        //        The deleted record should be completely deleted from Cookie DB.
        if (expires == 1000)
            continue; // skip deleted cookie

        String& name    = bits[5];
        const String& value   = (bits.size() > 6) ? bits[6] : emptyString; // value not always available
        String& domain  = bits[0];
        String& path    = bits[2];
        bool httpOnly  = bits[0].startsWith("#HttpOnly_");
        bool secure    = equalIgnoringCase(bits[3], "TRUE");
        bool session   = (!expires) ? true : false;

        if (httpOnly) 
            domain = domain.substring(10, domain.length() - 10); // skip "#HttpOnly_" preamble.

        if (!isMatchDomain(url, domain) || !url.path().startsWith(path, false))
            continue;

        rawCookies.append(Cookie(name, value, domain, path, expires, httpOnly, secure, session));
    }

    curl_slist_free_all(list);

    return true;
}

bool getRawCookies(const NetworkStorageSession&, const KURL& firstParty, const KURL& url, Vector<Cookie>& rawCookies)
{
    rawCookies.clear();

    if (!Manx::NetworkProfile::cookieEnabled()) 
        return false;

    // FIXME: CURL's cookie DB should not be used.
    //        More effective cookie DB is needed because CURL's DB is saved
    //        as a simple text with Netscape JAR format.
    //        It's not suitable for generic use of web browser.
    return getCookiesFromCurlCookieList(url, rawCookies);
}

void deleteCookie(const NetworkStorageSession&, const KURL& url, const String& deletedCookieName)
{
#if 0
    if (document->url().host() != url.host())
        return;
#endif
    
    String cookie;
    struct curl_slist *list;

    CURL* handle = curl_easy_init();
    curl_easy_setopt(handle, CURLOPT_SHARE, ResourceHandleManager::curlShareHandle());
    curl_easy_setopt(handle, CURLOPT_URL, url.string().latin1().data());
    
    ResourceHandleManager::lockCurlSharedResource(CURL_LOCK_DATA_COOKIE);
    curl_easy_getinfo(handle, CURLINFO_COOKIELIST, &list);
    ResourceHandleManager::unlockCurlSharedResource(CURL_LOCK_DATA_COOKIE);
    
    curl_easy_cleanup(handle);

    if (!list)
        return;

    for (struct curl_slist *cur = list; cur; cur = cur->next) {
        String data(cur->data);

        if (!cur->data)
            continue;

        Vector<String> bits;
        data.split("\t", bits);

        double expires = bits[4].toDouble() * 1000;
        // FIXME: a marker for deleted cookie is that "expires" value is 1.
        //        But the value can be easily set on JavaScript.
        //        The deleted record should be completely deleted from Cookie DB.
        if (expires == 1000)
            continue; // skip deleted cookie

        String& name    = bits[5];
        String& domain  = bits[0];
        String& path    = bits[2];
        bool httpOnly  = bits[0].startsWith("#HttpOnly_");

        if (httpOnly)
            domain = domain.substring(10, domain.length() - 10); // skip "#HttpOnly_" preamble.

        if (isMatchDomain(url, domain) && url.path().startsWith(path, false) && (name == deletedCookieName)) {
            // FIXME: The cookie specified by given "name" will still remain after this deletion.
            //        "expires=Thu, 01 Jan 1970 00:00:00 GMT" is used as deleted flag of cookies in the DB.
            //        More effective cookie DB is needed.
            String deletedCookieValue = deletedCookieName + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT";

            KURL deletedCookieUrl = url.copy();
            if (domain[0] == '.')
                deletedCookieUrl.setHost(domain);
            
            ResourceHandleManager::sharedInstance()->setCookies(deletedCookieUrl, deletedCookieValue);
            break;
        }
    }

    curl_slist_free_all(list);    
}

#if !PLATFORM(EFL)
void setCookieStoragePrivateBrowsingEnabled(bool enabled)
{
    // FIXME: Not yet implemented
    notImplemented();
}
#endif

void getHostnamesWithCookies(const NetworkStorageSession& session, HashSet<String>& hostnames)
{
    // FIXME: Not yet implemented
    notImplemented();
}

void deleteCookiesForHostname(const NetworkStorageSession&, const String& hostname)
{
    // FIXME: Not yet implemented
    notImplemented();
}

void deleteAllCookies(const NetworkStorageSession&)
{
#if PLATFORM(MANX)
    ResourceHandleManager::sharedInstance()->deleteAllCookies();
#else
    // FIXME: Not yet implemented
#endif
}

}

#endif // !USE(NTF)
